package crmdna.attendance;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.googlecode.objectify.ObjectifyFilter;

import crmdna.client.Client;
import crmdna.client.isha.IshaConfig;
import crmdna.common.APIException;
import crmdna.common.APIResponse.Status;
import crmdna.common.contactdetail.ContactDetailProp;
import crmdna.group.Group.GroupProp;
import crmdna.member.Member;
import crmdna.member.MemberCore.MemberProp;
import crmdna.practice.Practice;
import crmdna.practice.Practice.PracticeProp;
import crmdna.program.Program;
import crmdna.program.ProgramProp;
import crmdna.programtype.ProgramType;
import crmdna.programtype.ProgramTypeProp;
import crmdna.teacher.Teacher;
import crmdna.teacher.Teacher.TeacherProp;
import crmdna.user.User;
import crmdna.user.User.Action;
import crmdna.user.User.ResourceType;
import crmdna.venue.Venue;
import crmdna.venue.Venue.VenueProp;

public class AttendanceTest {
	
	private final LocalServiceTestHelper datastoreHelper =
		        new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
		        .setApplyAllHighRepJobPolicy());
	
	private final String client = "isha"; 	
 	private final String validUser = "valid@login.com";
 	private final String sgpUser = "sgpuser@login.com";
 	private final String klUser = "kluser@login.com";
 	private final String sgpAndKlUser = "sgpandkl@login.com";
 	
 	GroupProp sgp;
 	GroupProp kl;
 	
 	PracticeProp suryaNamaskar;
 	PracticeProp yogaAsanas;
 	PracticeProp shambhavi;
 	PracticeProp aumChanting;
 	PracticeProp ishaKriya;
 	
 	ProgramTypeProp innerEngineering7Day;
 	ProgramTypeProp suryaNamaskarAndAsanas;
 	ProgramTypeProp ishaKriyaTeacherLed;
 	ProgramTypeProp sathsang;
 	ProgramTypeProp shambhavi2Day;
 	
 	VenueProp giis;
 	VenueProp chaichee;
 	VenueProp gujarathiBhavan;
 	VenueProp yuhuaCC;
 	VenueProp woodlandsCC;
 	 	
 	TeacherProp muthu;
 	TeacherProp sharmila;
 	
 	MemberProp paramesh, thulasi, duane;
 	
 	ProgramProp ishaKriya20131229;
 	ProgramProp sathsang20140405;
 	ProgramProp shambhavi201405;
 	
    @Before
    public void setUp() {
        datastoreHelper.setUp();  
        ObjectifyFilter.complete();
        
        Client.create(client);
                   	
        sgp = crmdna.group.Group.create(client, "Singapore", User.SUPER_USER);
        assertEquals(1, sgp.groupId);
        
        kl = crmdna.group.Group.create(client, "KL", User.SUPER_USER);
        assertEquals(2, kl.groupId);
       
        User.create(client, validUser, sgp.groupId, User.SUPER_USER);
        assertEquals(1, User.get(client, validUser).toProp().userId);
        
        User.create(client, sgpUser, sgp.groupId, User.SUPER_USER);
        assertEquals(2, User.get(client, sgpUser).toProp().userId);
        
        User.addOrDeletePrivilege(client, sgpUser, ResourceType.PROGRAM, sgp.groupId, 
        		Action.WRITE, true, User.SUPER_USER);
        User.addOrDeletePrivilege(client, sgpUser, ResourceType.GROUP, sgp.groupId, 
        		Action.WRITE, true, User.SUPER_USER);
        
        User.create(client, klUser, kl.groupId, User.SUPER_USER);
        assertEquals(3, User.get(client, klUser).toProp().userId);
        
        User.addOrDeletePrivilege(client, klUser, ResourceType.PROGRAM, kl.groupId, 
        		Action.WRITE, true, User.SUPER_USER);
        User.addOrDeletePrivilege(client, klUser, ResourceType.GROUP, kl.groupId, 
        		Action.WRITE, true, User.SUPER_USER);
        
        User.create(client, sgpAndKlUser, sgp.groupId, User.SUPER_USER);
        assertEquals(4, User.get(client, sgpAndKlUser).toProp().userId);
        
        User.addOrDeletePrivilege(client, sgpAndKlUser, ResourceType.GROUP, kl.groupId, 
        		Action.WRITE, true, User.SUPER_USER);
        User.addOrDeletePrivilege(client, sgpAndKlUser, ResourceType.GROUP, sgp.groupId, 
        		Action.WRITE, true, User.SUPER_USER);
        
        suryaNamaskar = Practice.create(client, "Surya Namaskar", User.SUPER_USER);
        yogaAsanas = Practice.create(client, "Yoga Asanas", User.SUPER_USER);
        shambhavi = Practice.create(client, "Shambhavi", User.SUPER_USER);
        ishaKriya = Practice.create(client, "Isha Kriya", User.SUPER_USER);
        aumChanting = Practice.create(client, "Aum Chanting", User.SUPER_USER);
        
        Set<Long> practiceIds = new HashSet<>();
        practiceIds.add(shambhavi.practiceId);
        practiceIds.add(aumChanting.practiceId);
        innerEngineering7Day = ProgramType.create(client, "Inner Engineering 7 day", 
        		practiceIds, User.SUPER_USER);
        
        practiceIds.clear();
        practiceIds.add(suryaNamaskar.practiceId);
        practiceIds.add(yogaAsanas.practiceId);
        suryaNamaskarAndAsanas = ProgramType.create(client, "Hata Yoga (Surya Namaskar & Asanas)", 
        		practiceIds, User.SUPER_USER);
        
        practiceIds.clear();
        practiceIds.add(ishaKriya.practiceId);        
        ishaKriyaTeacherLed = ProgramType.create(client, "Isha Kriya", 
        		practiceIds, User.SUPER_USER);
        
        sathsang = ProgramType.create(client, "Sathsang", null, User.SUPER_USER);
        
        practiceIds.clear();
        practiceIds.add(shambhavi.practiceId);
        shambhavi2Day = ProgramType.create(client, "Shambhavi", practiceIds, User.SUPER_USER);
        
        giis = Venue.create(client, "GIIS", "GIIS", sgp.groupId, User.SUPER_USER);
        chaichee = Venue.create(client, "Chai Chee", "Chai Chee", sgp.groupId, User.SUPER_USER);
        gujarathiBhavan = Venue.create(client, "Gujarathi Bhavan", 
        		"Gujarathi Bhavan", sgp.groupId, User.SUPER_USER);
        yuhuaCC = Venue.create(client, "Yuhua CC", "Yuhua CC", sgp.groupId, User.SUPER_USER);
        woodlandsCC = Venue.create(client, "Woodlands CC", "Woodlands CC", sgp.groupId, User.SUPER_USER);
        
        muthu = Teacher.create(client, "muthu_sys@gmail.com", sgp.groupId, User.SUPER_USER);
        sharmila = Teacher.create(client, "sharmila@bhairaviyoga.sg", sgp.groupId, User.SUPER_USER); 
        
        //add members
        ContactDetailProp contactDetailProp = new ContactDetailProp();
    	contactDetailProp.email = "paramesh@ishafoundation.com";
    	contactDetailProp.centerId = sgp.groupId;
    	
        paramesh = Member.create(client, sgp.groupId, 
    			contactDetailProp, User.SUPER_USER);
    	assertEquals(1, paramesh.memberId);
    	
    	contactDetailProp = new ContactDetailProp();
    	contactDetailProp.email = "thulasidhar@gmail.com";
    	contactDetailProp.centerId = sgp.groupId;
    	
    	thulasi = Member.create(client, sgp.groupId, 
    			contactDetailProp, User.SUPER_USER);
    	assertEquals(2, thulasi.memberId);
    	
    	contactDetailProp = new ContactDetailProp();
    	contactDetailProp.email = "duane.bong@barclays.com";
    	contactDetailProp.centerId = sgp.groupId;
    	
        duane = Member.create(client, sgp.groupId, 
    			contactDetailProp, User.SUPER_USER);
    	assertEquals(3, duane.memberId);
    	
    	ishaKriya20131229 = Program.create(client, sgp.groupId, 
				ishaKriyaTeacherLed.programTypeId, 
				yuhuaCC.venueId, muthu.teacherId, 
				20131229, 20131229, 2, null, 0, null, sgpUser);
    	
    	sathsang20140405 = Program.create(client, sgp.groupId, sathsang.programTypeId, 
    			giis.venueId, sharmila.teacherId, 
    			20140405, 20140405, 1, null, 0, null, User.SUPER_USER);
    	
    	shambhavi201405 = Program.create(client, sgp.groupId,
    			shambhavi2Day.programTypeId, giis.venueId, sharmila.teacherId, 
    			20140503, 20140504, 1, null, 0, null, User.SUPER_USER);
    	
    	Member.addOrDeleteProgram(client, thulasi.memberId, shambhavi201405.programId,
    			true, User.SUPER_USER);
    	Member.addOrDeleteProgram(client, paramesh.memberId, shambhavi201405.programId,
    			true, User.SUPER_USER);
    	
    	Set<Long> sathsangPracticeIds = new HashSet<>();
    	sathsangPracticeIds.add(shambhavi.practiceId);
    	sathsangPracticeIds.add(ishaKriya.practiceId);
    	
    	IshaConfig.setSathsangPractices(sathsangPracticeIds, User.SUPER_USER);    	
    }

    @After
    public void tearDown() {       
    	ObjectifyFilter.complete();
        datastoreHelper.tearDown();
    }
    
    @Test
    public void checkInCheckOutAndGetNumCheckInsTest() {        	    	    	    	    	    	    	    
    	IAttendance impl = AttendanceFactory.getImpl(client);
    	
    	//num checkins should be 0
    	int numCheckins = impl.getNumCheckins(ishaKriya20131229.programId, 
    			20131229, 1);
    	assertEquals(0, numCheckins);
    	
    	//date should be valid
    	try {    		    	
    		impl.checkin(paramesh.memberId, 
    				ishaKriya20131229.programId, 20131230, 1, User.SUPER_USER);
    		assertTrue(false);
    	} catch (APIException ex){
    		assertEquals(Status.ERROR_RESOURCE_INCORRECTLY_SPECIFIED, ex.statusCode);
    	}
    	
    	//member should be valid
    	try {
    		impl.checkin(100, //invalid member id 
    				ishaKriya20131229.programId, 20131229, 1, User.SUPER_USER);
    		assertTrue(false);
    	} catch (APIException ex){
    		assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
    	}
    	
    	//program id should be valid
    	try {
    		impl.checkin(paramesh.memberId, 
    				ishaKriya20131229.programId + 100, 20131229, 1, User.SUPER_USER);
    		assertTrue(false);
    	} catch (APIException ex){
    		assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
    	}
    	
    	//batch no should be valid
    	try {
    		impl.checkin(paramesh.memberId, 
    				ishaKriya20131229.programId, 20131229, 3, User.SUPER_USER);
    		assertTrue(false);
    	} catch (APIException ex){
    		assertEquals(Status.ERROR_RESOURCE_INCORRECTLY_SPECIFIED, ex.statusCode);
    	}
    	
    	//user should have permission - kl user cannot checkin for singapore program
    	try {
    		impl.checkin(paramesh.memberId, 
    				ishaKriya20131229.programId, 20131229, 1, klUser);
    		assertTrue(false);
    	} catch (APIException ex){
    		assertEquals(Status.ERROR_INSUFFICIENT_PERMISSION, ex.statusCode);
    	}
    	
    	numCheckins = impl.checkin(paramesh.memberId, ishaKriya20131229.programId, 
    			20131229, 1, sgpUser);
    	assertEquals(1, numCheckins);
    	//this program should be tagged to member
    	paramesh = Member.safeGet(client, paramesh.memberId, User.SUPER_USER).toProp();
    	assertTrue(paramesh.programIds.contains(ishaKriya20131229.programId));
    	
    	//cannot checkin the same member again in same batch
    	try {
    		impl.checkin(paramesh.memberId, ishaKriya20131229.programId, 
        			20131229, 1, sgpUser);
    		assertTrue(false);
    	} catch (APIException ex) {
    		assertEquals(Status.ERROR_RESOURCE_ALREADY_EXISTS, ex.statusCode);
    	}
    	
    	//cannot checkin the same member again in batch 2
    	try {
    		impl.checkin(paramesh.memberId, ishaKriya20131229.programId, 
        			20131229, 2, sgpUser);
    		assertTrue(false);
    	} catch (APIException ex) {
    		assertEquals(Status.ERROR_RESOURCE_ALREADY_EXISTS, ex.statusCode);
    	}
    	
    	//can checkout
    	numCheckins = impl.checkout(paramesh.memberId, ishaKriya20131229.programId,
    			20131229, User.SUPER_USER);
    	//batch 1 count should go to zero
    	assertEquals(0, numCheckins);
    	//Member should not show this member as completed
    	paramesh = Member.safeGet(client, paramesh.memberId, User.SUPER_USER).toProp();
    	assertTrue(! paramesh.programIds.contains(ishaKriya20131229.programId));
    	
    	//can check in the same member in batch 2
    	numCheckins = impl.checkin(paramesh.memberId, ishaKriya20131229.programId, 
    			20131229, 2, User.SUPER_USER);
    	//batch 2 count should be 1, batch 1 should remain at 0
    	assertEquals(1, numCheckins);
    	assertEquals(0, impl.getNumCheckins(ishaKriya20131229.programId, 
    			20131229, 1));
    	paramesh = Member.safeGet(client, paramesh.memberId, User.SUPER_USER).toProp();
    	assertTrue(paramesh.programIds.contains(ishaKriya20131229.programId));
    	
    	//can checkin another member in batch 2
    	numCheckins = impl.checkin(thulasi.memberId, ishaKriya20131229.programId, 
    			20131229, 2, User.SUPER_USER);
    	//batch 2 count should be 2
    	assertEquals(2, numCheckins);
    	thulasi = Member.safeGet(client, thulasi.memberId, User.SUPER_USER).toProp();
    	assertTrue(thulasi.programIds.contains(ishaKriya20131229.programId));
    	
    	//for a program with multiple sessions, member record will be marked
    	//as complete only when checked in for all sessions
    	ProgramProp suryaKriya201403 = Program.create(client, sgp.groupId, 
				ishaKriyaTeacherLed.programTypeId, 
				yuhuaCC.venueId, sharmila.teacherId, 
				20140308, 20140309, 2, null, 0, null, sgpUser);
    	numCheckins = impl.checkin(paramesh.memberId, suryaKriya201403.programId, 
    			20140308, 1, sgpUser);
    	assertEquals(1, numCheckins);
    	paramesh = Member.safeGet(client, paramesh.memberId, sgpUser).toProp();
    	assertTrue(! paramesh.programIds.contains(suryaKriya201403.programId));
    	
    	//now check in for the second session
    	numCheckins = impl.checkin(paramesh.memberId, suryaKriya201403.programId, 
    			20140309, 1, sgpUser);
    	assertEquals(1, numCheckins);
    	paramesh = Member.safeGet(client, paramesh.memberId, sgpUser).toProp();
    	assertTrue(paramesh.programIds.contains(suryaKriya201403.programId));
    	
    	//checkout of second session
    	impl.checkout(paramesh.memberId, suryaKriya201403.programId,
    			20140309, sgpUser);
    	paramesh = Member.safeGet(client, paramesh.memberId, sgpUser).toProp();
    	assertTrue(! paramesh.programIds.contains(suryaKriya201403.programId));    	    			    	   	    	 	
    }
    
    @Test public void getMembersForCheckInTest() {
    	IAttendance impl = AttendanceFactory.getImpl(client);
    	
    	List<CheckInMemberProp> props = impl.getMembersForCheckIn(".com", 
    			sathsang20140405.programId, 20140405, null, User.SUPER_USER);     	
    	assertEquals(3, props.size());
    	
    	//should be sorted
    	assertEquals(duane.memberId, props.get(0).memberId);
    	assertEquals(false, props.get(0).allow);
    	assertTrue(props.get(0).notAllowingReason.toLowerCase().contains("not meditator"));    	
    	assertEquals(paramesh.memberId, props.get(1).memberId);
    	assertEquals(true, props.get(1).allow);
    	assertEquals(thulasi.memberId, props.get(2).memberId);
    	assertEquals(true, props.get(2).allow);
    	
    	//check in thulasi
    	impl.checkin(thulasi.memberId, sathsang20140405.programId,
    			20140405, 1, User.SUPER_USER);
    	props = impl.getMembersForCheckIn(".com", 
    			sathsang20140405.programId, 20140405, null, User.SUPER_USER);     	
    	assertEquals(thulasi.memberId, props.get(2).memberId);
    	assertEquals(false, props.get(2).allow);    	
    	assertTrue(props.get(2).notAllowingReason.toLowerCase().contains("checked in"));
    	
    	//now check out thulasi
    	impl.checkout(thulasi.memberId, sathsang20140405.programId, 
    			20140405, User.SUPER_USER);
    	props = impl.getMembersForCheckIn(".com", 
    			sathsang20140405.programId, 20140405, null, User.SUPER_USER);     	
    	assertEquals(thulasi.memberId, props.get(2).memberId);
    	assertEquals(true, props.get(2).allow);    	    	
    }                           
}